import zipfile
import pyzipper
import itertools
import string
import subprocess
import shutil
import sys

def extract_zip_with_zipfile(zip_file, password):
    try:
        with zipfile.ZipFile(zip_file, 'r') as zf:
            zf.extractall(pwd=password.encode('utf-8'))
        return True
    except (RuntimeError, zipfile.BadZipFile) as e:
        print(f"Failed with password: {password}, exception: {e}")
        return False

def extract_zip_with_pyzipper(zip_file, password):
    try:
        with pyzipper.AESZipFile(zip_file, 'r') as zf:
            zf.extractall(pwd=password.encode('utf-8'))
        return True
    except (RuntimeError, pyzipper.BadZipFile, pyzipper.LargeZipFile) as e:
        print(f"Failed with password: {password}, exception: {e}")
        return False

def extract_zip_with_7z(zip_file, password):
    try:
        result = subprocess.run(['7z', 'x', '-p{}'.format(password), zip_file], 
                                stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        if 'Everything is Ok' in result.stdout.decode('utf-8'):
            return True
        else:
            print(f"Failed with password: {password}, result: {result.stderr.decode('utf-8')}")
            return False
    except Exception as e:
        print(f"Exception with password: {password}, exception: {e}")
        return False

def brute_force_zip(zip_file, charset, max_length, extractor):
    total_combinations = sum(len(charset) ** length for length in range(1, max_length + 1))
    tried_combinations = 0

    for length in range(1, max_length + 1):
        for password_tuple in itertools.product(charset, repeat=length):
            password = ''.join(password_tuple)
            tried_combinations += 1
            print(f"Trying password: {password} ({tried_combinations}/{total_combinations})")
            if extractor(zip_file, password):
                print(f"Password found: {password}")
                return password
    print("Password not found.")
    return None

def detect_zip_method(zip_file):
    try:
        with zipfile.ZipFile(zip_file, 'r') as zf:
            if any(info.compress_type == pyzipper.ZIP_LZMA for info in zf.infolist()):
                return '7z'
            elif any(info.flag_bits & 0x1 for info in zf.infolist()):  # Check if any file is encrypted
                return 'pyzipper'
            else:
                return 'zipfile'
    except zipfile.BadZipFile as e:
        print(f"Bad ZIP file: {e}")
        return None

if __name__ == "__main__":
    zip_file_path = sys.argv[1]
    charset = sys.argv[2]
    max_length = int(sys.argv[3])

    zip_method = detect_zip_method(zip_file_path)
    if zip_method == 'zipfile':
        print("Using zipfile module for extraction.")
        extractor = extract_zip_with_zipfile
    elif zip_method == 'pyzipper':
        print("Using pyzipper module for extraction.")
        extractor = extract_zip_with_pyzipper
    elif zip_method == '7z':
        print("Using 7z command-line tool for extraction.")
        if not shutil.which('7z'):
            print("7z is not installed. Please install p7zip to proceed.")
            exit(1)
        extractor = extract_zip_with_7z
    else:
        print("Could not determine the ZIP file method or unsupported compression method.")
        exit(1)

    found_password = brute_force_zip(zip_file_path, charset, max_length, extractor)
    if found_password:
        print(f"Successfully cracked the password: {found_password}")
    else:
        print("Failed to crack the password.")

# pip install pyzipper
# pkg install p7zip or p7zip-full
# sansjtw